package jstask

import (
	"bytes"
	"errors"
	"os"
	"strings"

	"gitee.com/haodreams/golib/logs"
	"github.com/dop251/goja"
	"github.com/robfig/cron/v3"
)

type CallBack func(vm *goja.Runtime) any

//InitSyncJs 初始化同步的任务
/**
 * @description:
 * @param {string} path
 * @param {func} initFunc 并发执行时，重新初始化脚本时的回调初始化函数
 * @return {*}
 */
func InitCronJs(path string, initFunc CallBack) {
	if boss == nil {
		boss = new(Boss)
	}
	tasks := loadJsFile(path, "cron.csv", false, initFunc)
	array := make([]*Task, 0)
	kv := map[string]*Tasks{}
	for _, task := range tasks {
		if ts, ok := kv[task.Time]; ok {
			ts.tasks = append(ts.tasks, task)
		} else {
			ts = new(Tasks)
			ts.Time = task.Time
			ts.tasks = append(ts.tasks, task)
			kv[task.Time] = ts
		}
		array = append(array, task)
	}

	job := new(CronTask)
	job.array = array
	job.taskMap = kv
	job.cron = cron.New(cron.WithSeconds())
	boss.Job = job

	for _, tasks := range kv {
		if tasks.Time == "init" {
			go tasks.Run()
			continue
		}
		id, err := job.cron.AddJob(tasks.Time, tasks)
		if err != nil {
			logs.Error(err.Error())
			continue
		}
		tasks.ID = int(id)
	}
	job.cron.Start()
}

//InitSyncJs 初始化同步的任务
/**
 * @description:
 * @param {string} path
 * @param {func} initFunc 并发执行时，重新初始化脚本时的回调初始化函数
 * @return {*}
 */
func InitSyncJs(path string, initFunc CallBack) {
	if boss == nil {
		boss = new(Boss)
	}

	tasks := loadJsFile(path, "sync.csv", true, initFunc)
	array := make([]*Task, 0)
	kv := map[string]*Tasks{}
	for _, task := range tasks {
		if ts, ok := kv[task.Time]; ok {
			ts.tasks = append(ts.tasks, task)
		} else {
			ts = new(Tasks)
			ts.Time = task.Time
			ts.tasks = append(ts.tasks, task)
			kv[task.Time] = ts
		}
		array = append(array, task)
	}

	job := new(SyncTask)
	job.array = array
	job.taskMap = kv
	job.cron = cron.New(cron.WithSeconds())

	boss.Job = job

	for _, tasks := range kv {
		if tasks.Time == "init" {
			go tasks.Run()
			continue
		}
		id, err := job.cron.AddJob(tasks.Time, tasks)
		if err != nil {
			logs.Error(err.Error())
			continue
		}
		tasks.ID = int(id)
	}
	job.cron.Start()
}

// InitAsynJs 初始化异步任务
func InitAsynJs(path string, initCallback CallBack) {
	if boss == nil {
		boss = new(Boss)
	}

	tasks := loadJsFile(path, "async.csv", true, initCallback)

	job := new(AsyncTask)
	job.tasks = tasks                       // append(job.tasks, tasks...)
	job.cron = cron.New(cron.WithSeconds()) //

	boss.Job = job

	for _, task := range tasks {
		if task.Time == "init" {
			go task.Run()
			continue
		}
		id, err := job.cron.AddJob(task.Time, task)
		if err != nil {
			logs.Error(err.Error())
			continue
		}
		task.ID = int(id)
	}

	job.cron.Start()
}

// loadJsFile 初始化js
func loadJsFile(path string, fileName string, initd bool, initCallback func(vm *goja.Runtime) any) (tasks []*Task) {
	data, err := os.ReadFile(path + fileName)
	if err != nil {
		return
	}

	buf := bytes.NewBuffer(data)
	for line, err := buf.ReadString('\n'); err == nil || len(line) > 0; line, err = buf.ReadString('\n') {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		if line[0] == '#' {
			continue
		}

		//time,lua,desc
		ss := strings.Split(line, ",")
		for i := range ss {
			ss[i] = strings.TrimSpace(ss[i])
		}

		if len(ss) < 2 {
			continue
		}

		task := new(Task)
		task.Time = ss[0]
		task.Initd = initd
		task.initCallback = initCallback
		//是关键字段
		task.Time = strings.ReplaceAll(task.Time, "|", ",")
		task.Path = path + ss[1]

		if len(ss) > 2 {
			task.Desc = ss[2]
		}

		if initd {
			dada, err := os.ReadFile(task.Path)
			if err != nil {
				logs.Warn(err)
				task.Msg = err.Error()
				tasks = append(tasks, task)
				continue
			}

			vm := goja.New()
			var this any
			if initCallback != nil {
				this = initCallback(vm)
			}

			vm.Set("SetRunTimeout", func(timeout int64) {
				task.Timeout = timeout //设置执行的最大超时时间
			})

			_, err = vm.RunScript(ss[1], string(dada))
			if err != nil {
				logs.Warn(err)
				task.Msg = err.Error()
				tasks = append(tasks, task)
				continue
			}

			task.this = vm.ToValue(this)

			//自动检查是否有初始化函数
			setup, ok := goja.AssertFunction(vm.Get("setup"))
			if ok && setup != nil {
				_, err := setup(task.this)
				if err != nil {
					logs.Warn(err.Error())
					task.Msg = err.Error()
				}
			}

			run, ok := goja.AssertFunction(vm.Get("run"))
			if !ok {
				err = errors.New("run() not a function")
				logs.Warn(err)
				tasks = append(tasks, task)
				continue
			}
			task.run = run
			task.vm = vm
		}

		tasks = append(tasks, task)
	}
	return
}
